package com.kingyea.app.market.area.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONReader;
import com.google.common.collect.Lists;
import com.kingyea.app.market.area.bean.Area;
import com.kingyea.app.market.area.service.AreaService;
import com.kingyea.app.market.utils.StateCode;
import com.kingyea.mobilepolice.system.Result;
import com.kingyea.mobilepolice.system.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.FileReader;
import java.util.*;

/**
 * @author Mr.Lin
 * @version v1.0.0
 * @description Area 控制层
 * @date Created in 2018-03-25 21:47:16
 */
@RequestMapping(value = "/area")
@RestController
public class AreaController {

    /**
     * 所有 area 在redis的key前缀
     */
    public static final String area_prefix = "area:";
    /**
     * area 在redis的key前缀
     */
    public static final String area_code_prefix = area_prefix + "c:";
    /**
     * area子集:例如(中国:{所有的省级区域列表})  在redis的key前缀
     */
    public static final String area_subset_prefix = area_prefix + "s:";
    /**
     * area级联详情:例如(南山:{广东、深圳、南山})  在redis的key前缀
     */
    public static final String area_details_prefix = area_prefix + "d:";
    private static final Logger LOGGER = LoggerFactory.getLogger(AreaController.class);
    @Autowired
    private AreaService areaService;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询全部
     *
     * @param
     * @return
     */
    @RequestMapping(method = RequestMethod.GET)
    public List<Area> list() throws Exception {
        return areaService.list(null);
    }

    /**
     * 通过ID查找对象
     *
     * @param code
     * @return
     */
    @RequestMapping(value = "/{code}", method = RequestMethod.GET)
    public Area detail(@PathVariable Long code) throws Exception {
        return areaService.getById(code);
    }

    /**
     * 创建
     *
     * @param area
     * @return
     */
    @RequestMapping(method = RequestMethod.POST)
    public Area create(@RequestBody Area area) throws Exception {
        return areaService.save(area);
    }

    /**
     * 更新
     *
     * @param area
     * @return
     */
    @RequestMapping(method = RequestMethod.PUT)
    public Area update(@RequestBody Area area) throws Exception {
        return areaService.update(area);
    }

    /**
     * 删除
     *
     * @param code
     * @return
     */
    @RequestMapping(value = "/{code}", method = RequestMethod.DELETE)
    public int delete(@PathVariable Long code) throws Exception {
        return areaService.deleteById(code);
    }


    /**
     * 测试  重新从数据库载入area数据到redis
     *
     * @param
     * @return int
     * @author Mr.Lin
     * @date 2018/3/25 11:04
     */
    @RequestMapping(value = "/reload2Redis", method = RequestMethod.GET)
    public Object reload2Redis() throws Exception {
        long start = System.currentTimeMillis();
        List<Area> areaList = areaService.list(new Area());
        System.out.println("清空redis内所有区域数据，准备重新写入。");
        redisTemplate.delete(redisTemplate.keys(area_prefix + "*"));
        for (Area area : areaList) {
            redisTemplate.opsForValue().set(area_code_prefix + area.getCode(), JSON.toJSONString(area));
        }
        processAreaSubSet2Redis();
        processAreaDetail2Redis();
        return Result.getSuccessResult(" 重新从数据库载入area数据到redis,完成，耗时： " + (System.currentTimeMillis() - start));
    }


    /**
     * 测试初始化区域数据 ：执行前清空area表
     *
     * @param
     * @return int
     * @author Mr.Lin
     * @date 2018/3/25 11:04
     */
    @RequestMapping(value = "/init", method = RequestMethod.GET)
    public Object init() throws Exception {
//        List existAreas = areaService.list(new Area());
        if (true) {
            System.out.println("数据库中区域表无数据，开始导入：");
            importData2Redis();
            Set<String> keys = redisTemplate.keys(area_code_prefix + "*");
            List<String> areaStrList = redisTemplate.opsForValue().multiGet(keys);
            System.out.println("全部区域数据已写入redis，siez:" + areaStrList.size());
            Integer size = areaService.size();
            System.out.println("当前数据库记录大小：" + size);
            if (size.intValue() < areaStrList.size()) {
                System.out.println(JSON.toJSONString(areaStrList));
                for (String areaStr : areaStrList) {
                    System.out.println(areaStr);
                    areaService.save(JSON.parseObject(areaStr, Area.class));
                }
                System.out.println("全部区域数据已写入DB，siez:" + areaStrList.size());
            }
            return Result.getSuccessResult("init end ");
        } else {
            return Result.getSuccessResult("");
        }

    }


    /**
     * 行政区域数据导入
     * * IDEA 设置Compiler为Eclipse
     *
     * @param
     * @return void
     * @author Mr.Lin
     * @date 2018/3/25 1:57
     */
    private void importData2Redis() throws Exception {
        String dir = this.getClass().getClassLoader().getResource("").getPath();
        File addressData = new File(dir + "/testData/addressData.json");

        long startTime = System.currentTimeMillis();
        JSONReader reader = new JSONReader(new FileReader(addressData));
        System.out.println("文件读取耗时：" + (System.currentTimeMillis() - startTime));

        String areaData = reader.readString();
        System.out.println(areaData);

        startTime = System.currentTimeMillis();
        Map<String, String> areaMap = JSON.parseObject(areaData, HashMap.class);
        System.out.println("第一层转换耗时：" + (System.currentTimeMillis() - startTime));
        System.out.println("第一层List大小：" + areaMap.size());

        System.out.println("清空redis内所有区域数据，准备重新写入。");
        redisTemplate.delete(redisTemplate.keys(area_prefix + "*"));
        startTime = System.currentTimeMillis();
        processAreaDate("86", areaData, "0");//  第一条设置为 {}
        System.out.println("所有转换耗时：" + (System.currentTimeMillis() - startTime));

        processAreaSubSet2Redis();
        processAreaDetail2Redis();
    }

    /**
     * 保存基础数据到redis，
     *
     * @param code   区域编号
     * @param valStr 区域名字或者子集字符串
     * @param pCode  父级编号
     * @return java.util.List<com.kingyea.app.market.area.bean.Area>
     * @author Mr.Lin
     * @date 2018/3/25 14:16
     */
    private void processAreaDate(String code, String valStr, String pCode) throws Exception {
        JSONObject jsonObject = null;
        try {
            jsonObject = JSON.parseObject(valStr);
        } catch (Exception e) {
        }
        if (jsonObject == null) {
            /** 转换失败，代表他就是字符串，具体的地名 */
            String area_id = area_code_prefix + code;
            Area area = new Area(Long.valueOf(code), valStr, Long.valueOf(pCode));
//            areaList.add(area);// 内存不足，不对外返回list了，直接保存到redis
            redisTemplate.opsForValue().set(area_id, JSON.toJSONString(area));
        } else {
            /** 如果转换成功，那就代表他是集合 */
            for (String subKey : jsonObject.keySet()) {
                processAreaDate(subKey, jsonObject.get(subKey).toString(), code);
            }
        }
    }

    /**
     * Area 基础记录录入后生成 子集记录
     *
     * @param
     * @return java.util.List<com.kingyea.app.market.area.bean.Area>
     * @author Mr.Lin
     * @date 2018/3/25 14:17
     */
    private void processAreaSubSet2Redis() {
        System.out.println("重新生成子集记录start...");
        redisTemplate.delete(redisTemplate.keys(area_subset_prefix + "*"));
        List<String> areaStrList = redisTemplate.opsForValue().multiGet(redisTemplate.keys(area_code_prefix + "*"));
        redisTemplate.opsForValue().set(area_code_prefix + "86", JSON.toJSONString(new Area(86l, "中国", 0l)));

        for (String areaStr : areaStrList) {
            Area area = JSON.parseObject(areaStr, Area.class);
            String area_pid = area_subset_prefix + area.getPcode();
            String areaPid2Query = area_code_prefix + area.getCode();
            Object existPObj = redisTemplate.opsForValue().get(areaPid2Query);// 检查自己有没有pObj
            if (!StringUtils.isEmpty(existPObj)) {// 可能存在 ，就保存该pObj的子集记录：area_pid_XXX:[{},{}]
                List areas = Lists.newArrayList();
                Object existSubObj = redisTemplate.opsForValue().get(area_pid);// 检查 子集记录是否存在
                if (!StringUtils.isEmpty(existSubObj)) {
                    areas = JSON.parseObject(existSubObj.toString(), List.class);
                }
                areas.add(area);
                redisTemplate.opsForValue().set(area_pid, JSON.toJSONString(areas));
            } else {
                if (area.getCode().intValue() != 0)
                    throw new BusinessException(StateCode.SYSTEM_ERROR, "Redis 中 " + area_code_prefix + area.getCode() + " 数据异常,父级对象不存在：" + JSON.toJSONString(area));
            }
        }
        System.out.println("重新生成子集记录 end...");
    }


    /**
     * Area 基础记录录入后生成生成 对应的级联集合
     * 例如：宝安区:{广东、深圳、宝安区}
     *
     * @param
     * @return java.util.List<com.kingyea.app.market.area.bean.Area>
     * @author Mr.Lin
     * @date 2018/3/25 14:17
     */
    private void processAreaDetail2Redis() {
        System.out.println("删除所有的级联详情，重新生成 ... ");
        redisTemplate.delete(redisTemplate.keys(area_details_prefix + "*"));
        List<String> areaStrList = redisTemplate.opsForValue().multiGet(redisTemplate.keys(area_code_prefix + "*"));
        for (String areaStr : areaStrList) {
            Area area = JSON.parseObject(areaStr, Area.class);
            List<Area> areaDetails = Lists.newArrayList();// 等待保存的列表，保存前需要排序
            detail(areaDetails, area);
            areaDetails.sort(Comparator.comparing(Area::getCode));
            redisTemplate.opsForValue().set(area_details_prefix + String.valueOf(area.getCode()), JSON.toJSONString(areaDetails));
        }
        System.out.println("级联详情生成完毕 ... ");
    }

    private void detail(List<Area> areaDetails, Area area) {
        areaDetails.add(area);
        String pcode = String.valueOf(area.getPcode());
        if (!StringUtils.isEmpty(pcode)/*&&!pcode.equals("0")*/) {
            Object parentObj = redisTemplate.opsForValue().get(area_code_prefix + pcode);
            if (!StringUtils.isEmpty(parentObj)) {
                detail(areaDetails, JSON.parseObject(String.valueOf(parentObj), Area.class));
            }
        }
    }


}
